package com.ztjava.plugin.event;

import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;

import com.jfinal.kit.Ret;
import com.jfinal.log.Log;
import com.jfinal.plugin.redis.Cache;
import com.jfinal.plugin.redis.Redis;
import com.ztjava.core.Constant;
import com.ztjava.plugin.event.core.ApplicationEvent;
import com.ztjava.plugin.event.core.ApplicationListener;
import com.ztjava.plugin.event.core.AsyncResponseEvent;
import com.ztjava.plugin.event.service.EventService;
import com.ztjava.plugin.event.utils.ArrayListMultimap;


@SuppressWarnings("rawtypes")
public class EventKit {
	private static Log log = Log.getLog(EventKit.class);
	private static String cacheName = "EVENT";
	private static ArrayListMultimap<EventType, ListenerHelper> map;
	private static ExecutorService pool;
	private static Map<String,List<EventService>> serviceMap = new HashMap<>();
	private static Map<String,EventService> serviceCacheMap = new HashMap<>();
	private static String serverRmi;
	
	public static void setServerRim(String rmi){
		EventKit.serverRmi = rmi;
	}
	
	static void init(ArrayListMultimap<EventType, ListenerHelper> map, ExecutorService pool,String cacheName) {
		EventKit.map = map;
		EventKit.pool = pool;
		EventKit.cacheName = cacheName;
	}
	
	public interface EventCall {
		Ret exec(ApplicationListener listener,String response);
	}
	/**
	 * 事件执行方法
	 * @param eventType 事件类型
	 * @param call EventCall
	 */
	private static List<Ret> eventActuator(final EventType eventType, final EventCall call) {
		Collection<ListenerHelper> listenerList = map.get(eventType);
		List<Ret> result = new ArrayList<>();
		for (final ListenerHelper helper : listenerList) {
			if (null != pool && helper.enableAsync) {
				pool.execute(new Runnable() {
					public void run() {
						call.exec(helper.listener,helper.response);
					}
				});
			} else {
				//System.out.println(pool);
				result.add(call.exec(helper.listener,null));
			}
		}
		return result;
	}
	
	public static List<Ret> post(final String tag, final ApplicationEvent event) {
		return post(new EventType(tag, event.getClass()), event);
	}
	
	/**
	 * 发布事件(本地执行)
	 * @param eventType 事件封装
	 */
	@SuppressWarnings("unchecked")
	private static List<Ret> post(final EventType eventType, final ApplicationEvent event) {
		return EventKit.eventActuator(eventType, new EventCall() {
		
			public Ret exec(ApplicationListener listener,String response) {
				Ret ret = listener.onApplicationEvent(event);
				if(response!=null&&!response.equals("")){
					EventKit.postRemote(response, new AsyncResponseEvent(ret));
				}
				return ret;
			}
		});
	}

	public static Ret postRemoteOnly(final String tag, final ApplicationEvent event) {
		List<Ret> result = postRemote(tag, event);
		if(result.size()==0){
			return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE, "没有任何响应！");
			
		}
		return result.get(0);
	}
	/**
	 * 发布远程事件
	 * @param tag 标记
	 * @param event 事件
	 * @since 1.5.0
	 */
	public static List<Ret> postRemote(final String tag, final ApplicationEvent event) {
		List<Ret> result = new ArrayList<>();
		try {
			String eventName = event.getClass().getName();
			List<EventService> eventServiceList = serviceMap.get(eventName+"."+tag);
			if(eventServiceList==null){
				Cache cache = Redis.use();
				Map<String,Map<String,String>> eventMap = cache.hget(cacheName, eventName);
				if(eventMap!=null&&eventMap.containsKey(tag)){
					eventServiceList = new ArrayList<>();
					for(String key:eventMap.get(tag).keySet()){
						if(serverRmi!=null&&key.equals(serverRmi)){
							continue;
						}
						if(serviceCacheMap.containsKey(key)){
							eventServiceList.add(serviceCacheMap.get(key));
						}else {
							try{
								EventService eventService =(EventService)Naming.lookup(key);
								serviceCacheMap.put(key, eventService);
								eventServiceList.add( serviceCacheMap.get(key));
							}catch(NotBoundException e){
								e.printStackTrace();
								log.error(key+":NotBind!");
							}catch (Exception e){
								e.printStackTrace();
								log.error(key+":ERROR!");
							}
						}
					}
				}
			}
			result.addAll(post(tag, event));
			if(eventServiceList!=null){
				for(int i=0;i<eventServiceList.size();i++){
					result.addAll(eventServiceList.get(i).post(tag, event));
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}
}
